package coreAlgorithm;

import java.util.Iterator;

import javax.naming.OperationNotSupportedException;

public class ID implements Iterable<Bit> {
	
	@Override
	public int hashCode() {
		return integerValue();
		// TODO Implement a better hash code
	};
	
	/**
	 * The most-significant Bit is on the left and has the index 0
	 */
	private final Bit[] id; // If this is not final, review NodeTriple's
							// clone()-method and other dependent clone()-methods
	public static final int maxLength = 160;
	public static final ID LargestID = new ID("1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");
	public static final ID SmallestID = new ID("");

	public static ID applyXOR(ID id1, ID id2) {
		Bit[] result = new Bit[maxLength];
		for (int x = 0; x < maxLength; x++) {
			if (id1.getBitOnPosition(x).equals(id2.getBitOnPosition(x))) {
				result[x] = new Bit(0);
			} else {
				result[x] = new Bit(1);
			}
		}
		return new ID(result);

	}

	public ID(Bit[] value) {
		super();
		id = value;
	}

	public ID(String value) {
		super();
		id = new Bit[maxLength];
		final int diff = maxLength - value.length();
		if (diff < 0)
			throw new IllegalArgumentException("The length of the value was larger than the allowed maximum of " + maxLength);

		for (int x = 0; x < maxLength; x++) {
			if (x < diff) {
				id[x] = new Bit(0);
			} else {
				id[x] = new Bit(new Integer(Character.toString(value.charAt(x - diff))));
			}
		}
	}

	/**
	 * The parameter for the ID is evaluated as a decimal number
	 * 
	 * @param value
	 */
	public ID(int value) {
		this(Integer.toBinaryString(value));
	}

	public ID applyXOR(ID id) {
		return ID.applyXOR(this, id);
	}

	public int getValueOnPosition(int index) {
		return this.getBitOnPosition(index).getValue();
	}

	public Bit getBitOnPosition(int index) {
		if (index >= maxLength || index < 0)
			throw new IndexOutOfBoundsException("Allowed maximum is less than " + maxLength);
		return id[index];
	}

	public boolean equals(ID cmpId) {
		for (int x = 0; x < maxLength; x++) {
			if (!this.getBitOnPosition(x).equals(cmpId.getBitOnPosition(x)))
				return false;
		}
		return true;
	}

	public boolean equals(Object cmpObj) {
		if (cmpObj instanceof ID) {
			return equals((ID) cmpObj);
		} else {
			return false;
		}
	}
	
	public String binaryValue() {
		String result = "";
		Iterator<Bit> iter = this.iteratorOnMostSignificantBit();
		while (iter.hasNext()) {
			result += iter.next();
		}
		return result;
	}
	
	public int integerValue() {
		int result = 0;
		for (Iterator<Bit> iter = iteratorOnMostSignificantBit(); iter.hasNext();) {
			result *= 2;
			result += iter.next().getValue();			
		}
		return result;
	}

	public String toString() {
		// String result = "ID: ";
		return binaryValue();
	}

	/**
	 * Return the offset of the most significant bit, seen from the right.
	 * Big-endian! This is not equal to the position of a bit, which is
	 * calculated from the right, as it is an array!
	 * 
	 * @return
	 */
	public int getRightOffsetOfMostSignificantBit() {
		int x = maxLength;
		Iterator<Bit> iter = this.iterator();
		while (iter.hasNext()) {
			Bit bit = (Bit) iter.next();
			x--;
			if (bit.getValue() == 1)
				break;
		}
		return x;
	}

	@Override
	public Iterator<Bit> iterator() {
		return new IDIterator(this);
	}

	public Iterator<Bit> iteratorOnMostSignificantBit() {
		return new IDIterator(this, maxLength - 1 - this.getRightOffsetOfMostSignificantBit());
	}

	public Iterator<Bit> iterator(int startIndex) {

		return new IDIterator(this, startIndex);
	}

	private class IDIterator implements Iterator<Bit> {
		int index;
		ID myID;

		public IDIterator(ID id, int startIndex) {
			if (startIndex < 0 || startIndex >= maxLength)
				throw new IndexOutOfBoundsException("Allowed maximum is less than " + maxLength);
			index = startIndex;
			myID = id;
		}

		public IDIterator(ID id) {
			this(id, 0);
		}

		@Override
		public boolean hasNext() {
			return (index <= maxLength - 1);
		}

		@Override
		public Bit next() {
			return myID.getBitOnPosition(index++);
		}

		@Override
		public void remove() {
			// Not supported
			throw new RuntimeException(new OperationNotSupportedException("Remove operation is not intended for this iterator"));
		}

	}
}
